package com.xwaf.platform.system.provider.util;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSourceFactory;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class DBHelper {

	private static DataSource dataSource = null;
	static {
		try {
			initDataSource();
		} catch (Exception e) {
			log.error("加载数据库属性元素构建数据源失败！！！", e);
		}
	}

	/**
	 * 
	 * <p>
	 * 初始化数据源
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:08:45 <br>
	 * @see 无<br>
	 * @throws Exception
	 * @demo <br>
	 */
	private static void initDataSource() throws Exception {
		// String activeProfile = SpringContextUtil.getActiveProfile();
		// System.err.println(activeProfile);
		String activeProfile = "local";
		Properties props = new Properties();
		props.load(DBHelper.class.getClassLoader().getResourceAsStream("dbcp-" + activeProfile + ".properties"));
		// 使用连接池技术, 数据源DBCP
		dataSource = BasicDataSourceFactory.createDataSource(props);
	}

	/**
	 * 
	 * <p>
	 * 建立连接
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:08:53 <br>
	 * @see 无<br>
	 * @return
	 * @demo <br>
	 */
	public static Connection getConn() {
		Connection con = null;
		int tryTimes = 0; // 重试次数
		try {
			// 2.建立与数据库的 连接
			// 使用连接池技术, 数据源DBCP
			while (dataSource == null && tryTimes <= 3) {
				tryTimes++;
				log.error("----------------获取数据源失败，重试第" + tryTimes + "次-------------------");
				initDataSource();
				Thread.sleep(2000); // 停2秒
			}
		} catch (Exception e) {
			log.error("创建数据源连接失败！！！", e);
		}
		try {
			con = dataSource.getConnection();
		} catch (Exception e) {
			log.error("获取数据库连接失败！！！", e);
		}
		return con;
	}

	/**
	 * 
	 * <p>
	 * 关闭操作
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:09:02 <br>
	 * @see 无<br>
	 * @param con
	 *            数据库连接
	 * @param st
	 *            sql执行工具
	 * @param rs
	 *            返回结果集
	 * @demo <br>
	 */
	public static void close(Connection con, Statement st, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {
				log.error("关闭结果集失败！！！", e);
			}
		}
		if (st != null) {
			try {
				st.close();
			} catch (SQLException e) {
				log.error("关闭执行工具失败！！！", e);
			}
		}
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				log.error("关闭数据库连接失败！！！", e);
			}
		}
	}

	/**
	 * <p>
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:09:24 <br>
	 * @see 无<br>
	 * @param sql
	 *            要执行的sql语句 insert, update, delete)
	 * @param params
	 *            执行sql语句需要的参数
	 * @return 执行sql语句受影响的行数
	 * @demo <br>
	 */
	public static int doUpdate(String sql, Object... params) {
		Connection con = null;
		PreparedStatement st = null;
		int result = 0;
		try {
			con = getConn();
			log.debug("要执行sql语句：" + sql);
			st = con.prepareStatement(sql);
			setParams(st, params); // 设置参数
		} catch (SQLException e) {
			log.error("sql执行工具创建失败！！！", e);
		}
		try {
			result = st.executeUpdate(); // 执行sql , 针对insert, delete,update,
											// 返回结果是受影响行数
			log.debug("插入数据成功，插入数据的条数是：：" + result);
		} catch (SQLException e) {
			log.error("插入数据失败!!!", e);
		} finally {
			// 5.关闭连接
			DBHelper.close(con, st, null);
		}
		return result;
	}

	/**
	 * 
	 * <p>
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:09:43 <br>
	 * @see 无<br>
	 * @param sql
	 *            要执行的sql语句
	 * @param objs
	 *            执行sql语句需要的参数
	 * @return 取出数据库的数据,每一条记录是一个map : key是字段名或字段别名(小写字母), value应对字段的值
	 * @demo <br>
	 */
	public static List<Map<String, Object>> doQuery(String sql, Object... objs) {
		Connection con = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		List<Map<String, Object>> results = null;
		try {
			con = getConn();
			st = con.prepareStatement(sql); // 3.sql执行工具
			setParams(st, objs);
		} catch (SQLException e) {
			log.error("sql执行工具创建失败！！！", e);
		}
		try {
			rs = st.executeQuery(); // 4.执行sql取到返回数据白结果集
		} catch (SQLException e) {
			log.error("执行sql取到返回数据失败！！！", e);
		}
		try {
			ResultSetMetaData rsmd = rs.getMetaData(); // 元数据; 对象取取到的结果集数据的描述
			int cloumCount = rsmd.getColumnCount();
			results = new ArrayList<Map<String, Object>>();
			while (rs.next()) { // 判断结果集是否还有数据 (数据是一条记录的方式取出)
				Map<String, Object> record = new HashMap<String, Object>();
				for (int i = 1; i <= cloumCount; i++) {
					// rsmd.getColumnName(i) ：表的字段名或字段别名
					// rs.getObject(i) : 取到字段对应的值
					record.put(rsmd.getColumnName(i).toLowerCase(), rs.getObject(i));
				}
				results.add(record);
			}
		} catch (SQLException e) {
			log.error("取出结果集数据失败！！！", e);
		} finally {
			DBHelper.close(con, st, rs);
		}
		return results;
	}

	/**
	 * 
	 * <p>
	 * <p>
	 * 
	 * @author 李聪 <br>
	 * @email xwaf_love@yeah.net <br>
	 * @since JDK 1.8<br>
	 * @date 2019年12月30日 上午9:10:09 <br>
	 * @see 无<br>
	 * @param sql
	 *            要执行的sql语句
	 * @param objs
	 *            执行sql语句需要的参数
	 * @return 取出数据库的数据, key是字段名或字段别名(小写字母), value应对字段的值
	 * @demo <br>
	 */
	public static Map<String, Object> doQueryOne(String sql, Object... objs) {
		Connection con = null;
		PreparedStatement st = null;
		ResultSet rs = null;
		Map<String, Object> results = null;
		try {
			con = getConn();
			st = con.prepareStatement(sql); // 3.sql执行工具
			setParams(st, objs);
		} catch (SQLException e) {
			log.error("sql执行工具创建失败！！！", e);
		}
		try {
			rs = st.executeQuery(); // 4.执行sql取到返回数据白结果集
		} catch (SQLException e) {
			log.error("执行sql取到返回数据失败！！！", e);
		}
		try {
			ResultSetMetaData rsmd = rs.getMetaData(); // 元数据; 对象取取到的结果集数据的描述
			int cloumCount = rsmd.getColumnCount();
			if (rs.next()) { // 判断结果集是否还有数据 (数据是一条记录的方式取出)
				results = new HashMap<String, Object>();
				for (int i = 1; i <= cloumCount; i++) {
					// rsmd.getColumnName(i) ：表的字段名或字段别名
					// rs.getObject(i) : 取到字段对应的值
					results.put(rsmd.getColumnName(i).toLowerCase(), rs.getObject(i));
				}
			}
		} catch (SQLException e) {
			log.error("取出结果集数据失败！！！", e);
		} finally {
			DBHelper.close(con, st, rs);
		}
		return results;
	}

	private static void setParams(PreparedStatement st, Object... objs) {
		// 判断是否有参数
		if (objs == null || objs.length == 0) {
			return;
		}
		int flag = 0;
		try {
			for (int i = 0; i < objs.length; i++) {
				flag = i + 1;
				if (objs[i] == null) {
					st.setObject(i + 1, null);
					continue;
				}
				String paramType = objs[i].getClass().getName(); // 获得参数的类型
				if (Integer.class.getName().equals(paramType)) { // 判断是否是int类型
					st.setInt(i + 1, (int) objs[i]);
				} else if (Double.class.getName().equals(paramType)) { // 判断是否是dobule类型
					st.setDouble(i + 1, (double) objs[i]);
				} else if (String.class.getName().equals(paramType)) { // 判断是否是string类型
					st.setString(i + 1, (String) objs[i]);
				} else {
					st.setObject(i + 1, objs[i]);
				}
			}
		} catch (SQLException e) {
			log.error(String.format("注入第%d值时失败!!!", flag), e);
		}
	}

	public static void main(String[] args) {
		Map<String, Object> doQueryOne = DBHelper.doQueryOne("select * from CRGS_RECORDINFO t where t.id=?",
				"11631284");
		System.out.println(doQueryOne);
	}
}